﻿using System.Collections.ObjectModel;
using System.Threading;
using Simhopp.Client;
using Simhopp.Common;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;

namespace Simhopp.Client
{
    /// <summary>
    /// Implements the logic for handling commands for the Presenter class
    /// </summary>
    public partial class Presenter : IPresenterBindings, INotifyPropertyChanged
    {
        /// <summary>
        /// Handles the NextCompetitor command. @deprecated no longer used.
        /// </summary>
        /// <param name="sender">The invoking element</param>
        /// <param name="e">The event arguments</param>
        protected override void NextCompetitorHandler(object sender, ExecutedRoutedEventArgs e)
        {
            _serverListener.SendObject(new NetworkCommand(Commands.NextDiver));
        }
        /// <summary>
        /// Decides if the NextCompetitor can be executed. @deprecated no longer used.
        /// </summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void CanHandleNextCompetitor(object sender, CanExecuteRoutedEventArgs e)
        {
            if (Competition.CurrentRound == Rounds.Configure)
            {
                e.CanExecute = false;
            }
            else
            {
                if (Competition.GetCurrentDiver().CurrentRoundIsScored(Competition.CurrentRound, Competition.NumberOfJudges) &&
                    (Competition.CurrentDiverIndex + 1 < Competition.Participants.Count) )
                {
                    e.CanExecute = true;
                }
                else
                {
                    e.CanExecute = false;
                }
            }
        }

        /// <summary>
        /// Handles the NextRound command. @deprecated no longer used.
        /// </summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void NextRoundHandler(object sender, ExecutedRoutedEventArgs e)
        {
            _serverListener.SendObject(new NetworkCommand(Commands.NextRound));
        }

        /// <summary>
        /// Decides the the NextRound command can be invoked. @deprecated no longer used.
        /// </summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void CanHandleNextRound(object sender, CanExecuteRoutedEventArgs e)
        {
            if (Competition.CurrentRound == Rounds.Configure)
            {
                e.CanExecute = Competition.IsConfigured();
            }
            else if (Competition.CurrentRound == Rounds.ThirdRound)
            {
                e.CanExecute = false;
            }
            else
            {
                e.CanExecute = Competition.CurrentRoundIsScored();
            }
        }

        /// <summary>
        /// Handles the SetScore command.
        /// </summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments. The score should be passed as the parameter</param>
        protected override void SetScoreHandler(object sender, ExecutedRoutedEventArgs e)
        {
            var value = (double)e.Parameter;
            var score = new Score(ActiveJudge, value);
            ActiveJudge.HasSubmittedScore = true;
            _serverListener.SendObject(new NetworkCommand(Commands.AddScore, score));
        }

        /// <summary>
        /// Decides if the SetScore command can be invoked. 
        /// <summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void CanHandleSetScore(object sender, CanExecuteRoutedEventArgs e)
        {
    
            e.CanExecute = false;
            if (Competition.CurrentRound == Rounds.Configure)
            {
                return;
            }
            foreach (var score in Competition.GetCurrentDiver().Dives[(int)Competition.CurrentRound].Scores)
            {
                if (ActiveJudge.Equals(score.Judge)){
                    return;
                }
            }
            e.CanExecute = true;
        }

        /// <summary>
        /// Handles the AddCompetitor command.
        /// <summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments. A listbox may be passed as a parameter, and if so the last element will be selected.</param>
        protected override void AddCompetitorHandler(object sender, ExecutedRoutedEventArgs e)
        {
            Competition.AddParticipant(
                new Diver
                {
                    Name = "Ny tävlande",
                    Gender = Competition.Gender
                }
            );
            var p = (e.Parameter as ListBox);
            if (p != null)
            {
                // Determine if a valid index is returned. Select the item if it is valid.
                p.SelectedIndex = p.Items.Count - 1;
            }
        }

        /// <summary>
        /// Decides if it is possible to add a competitor. Always true. 
        /// <summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void CanHandleAddCompetitor(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }


        /// <summary>
        /// Handles the LogOnCommand.
        /// <summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void LogOnHandler(object sender, ExecutedRoutedEventArgs e)
        {
            var controlInputMap = new Collection<MutableKeyValuePair<String, Object>>
            {
                new MutableKeyValuePair<String, Object>("Användarnamn", ""),
                new MutableKeyValuePair<String, Object>("Lösenord", "")
            };
            var b = new InputBox("Inloggning", "Skriv in dina inloggingsuppgifter som du har fått av administratören. Observera att både lösenord och användarnamn är skiftlägeskänsliga.", controlInputMap);
            b.ShowDialog();
            LogOnInfo logonInfo = new LogOnInfo((string)controlInputMap[0].Value, (string)controlInputMap[1].Value);
            if (!logonInfo.UserName.Equals(""))
                _serverListener.SendObject(new NetworkCommand(Commands.Login, logonInfo));
        }

        /// <summary>
        /// Decides if the LogOn command can be invoked.
        /// <summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void CanHandleLogOn(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute=false;
            if (ActiveJudge.UserName.Equals("") && _serverListener.TcpClient.Connected)
                e.CanExecute = true;
        }

        /// <summary>
        /// Handles the LogOut command. A tab control may be passed as parameter, and if so the first tab will have focus after invocation.
        /// <summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments </param>
        protected override void LogoutHandler(object sender, ExecutedRoutedEventArgs e)
        {
            var tabControl = e.Parameter as TabControl;
            _serverListener.SendObject(new NetworkCommand(Commands.Logout));
            if (tabControl != null)
                tabControl.SelectedIndex = 0;
            ProcessLoginVerified(new User());
            UserInfo = "Du är ej inloggad";
        }

        /// <summary>
        /// Decides if the LogOut command can be invoked.
        /// <summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void CanHandleLogout(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
            if(ActiveJudge.UserName.Equals(""))
                e.CanExecute = false;
        }

        /// <summary>
        /// Handler for the AddJudge command. A listbox may be passed as command parameter, in which case the last item will be 
        /// <summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void AddJudgeHandler(object sender, ExecutedRoutedEventArgs e)
        {

            Application.Current.Dispatcher.BeginInvoke((Action)delegate()
            {
                try
                {     
                Competition.AddJudge(
                    new User
                    {
                        Name = "Ny domare",
                        AccountType = AccountTypes.Judge
                    }
                    );
                }

                catch (JudgeListFullException)
                {
                    MessageBox.Show(String.Format("Det går inte att lägga till fler än {0} domare",
                        Competition.NumberOfJudges));
                }
            });

            

                
            var p = (e.Parameter as ListBox);
            if (p != null)
            {
                
                p.SelectedIndex = p.Items.Count - 1;
            }


            //Used to update the ListBoxes in the tab for changing access level between judge/headjudge.
            NotifyPropertyChanged("AccessLevelHeadJudge");
            NotifyPropertyChanged("AccessLevelJudge");
            NotifyPropertyChanged("JudgesOnly");
        }


        /// <summary>
        /// Decides if the AddJudge command may be executed.
        /// <summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void CanHandleAddJudge(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }

        /// <summary>
        /// Handles the AddHeadJudge command. May take a listbox as a parameter, in which case the newly added element will be selected in the listbox.
        /// </summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void AddHeadJudgeHandler(object sender, ExecutedRoutedEventArgs e)
        {
            var p = (e.Parameter as ListBox);
            if (p != null)
            {
                Competition.AddJudge(
                    new User
                    {
                        Name = "Ny huvuddomare",
                        AccountType = AccountTypes.HeadJudge
                    }
                );
                p.SelectedItem = Competition.Judges.Last();
            }


            //Used to update the ListBoxes in the tab for changing access level between judge/headjudge.
            NotifyPropertyChanged("AccessLevelHeadJudge");
            NotifyPropertyChanged("AccessLevelJudge");
        }


        /// <summary>
        /// Handles the ChangeLevelToJudge command. The calling object should pass a listbox with the HeadJudge that should be changed as selected item. 
        /// </summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void ChangeLevelToJudgeHandler(object sender, ExecutedRoutedEventArgs e)
        {
            List<User> headJudgeList = new List<User>();

            foreach (var tempJudge in Competition.Judges)
            {
                if(tempJudge.AccountType == AccountTypes.HeadJudge)
                    headJudgeList.Add(tempJudge);
            }

            var p = (e.Parameter as ListBox); 

            if (p != null)
            {

                if (p.SelectedIndex >= 0)
                {
                    headJudgeList[p.SelectedIndex].AccountType = AccountTypes.Judge;

                    
                }
            }

            NotifyPropertyChanged("AccessLevelHeadJudge");
            NotifyPropertyChanged("AccessLevelJudge");
        }

        /// <summary>
        /// Handles the ChangeLevelToHead command. The calling object should pass a listbox with the Judge that should be changed as selected item. 
        /// </summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void ChangeLevelToHeadHandler(object sender, ExecutedRoutedEventArgs e)
        {
            List<User> judgeList = new List<User>();

            foreach (var tempJudge in Competition.Judges)
            {
                if (tempJudge.AccountType == AccountTypes.Judge)
                    judgeList.Add(tempJudge);
            }

            var p = (e.Parameter as ListBox);

            if (p != null)
            {
                if (p.SelectedIndex >= 0)
                    judgeList[p.SelectedIndex].AccountType = AccountTypes.HeadJudge;
            }

            NotifyPropertyChanged("AccessLevelHeadJudge");
            NotifyPropertyChanged("AccessLevelJudge");
        }



        /// <summary>
        /// Decides if the ChangeLevelToHead and the ChangeLevelToJudge command can be executed. 
        /// </summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void CanHandleChangeLevel(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }
        /// <summary>
        /// Decides if the CanHandleAddHeadJudge command can be executed. 
        /// </summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void CanHandleAddHeadJudge(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }

        /// <summary>
        /// Handles the exit command, closing the client.
        /// </summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void ExitHandler(object sender, ExecutedRoutedEventArgs e)
        {
            this.ExitGraceful();
            Environment.Exit(0);
        }

        /// <summary>
        /// Decides if the exit command can be executed.
        /// </summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void CanHandleExit(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }
        /// <summary>
        /// Handles the save command, bouncing the data to the server.
        /// </summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void SaveHandler(object sender, ExecutedRoutedEventArgs e)
        {
            _serverListener.SendObject(new NetworkCommand(Commands.Competition, this.Competition));
        }

        /// <summary>
        /// Decides if the Save command can be executed.
        /// </summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void CanHandleSave(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
            if (this.Competition.NotInProgress.CompareTo("false") == 0)
            {
                e.CanExecute = false;
            }
        }

        /// <summary>
        /// Decides if the FinalizeConfiguration command can be executed.
        /// </summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void CanHandleFinalizeConfiguration(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = false;
            if (Competition.CurrentRound == Rounds.Configure && Competition.Participants.Count != 0)
            {
                e.CanExecute = Competition.IsConfigured();
            }
        }

        /// <summary>
        /// Decides if the FinalizeConfiguration command can be executed.
        /// </summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void FinalizeConfigurationHandler(object sender, ExecutedRoutedEventArgs e)
        {
            this.Competition.NotInProgress = "false";
            _serverListener.SendObject(new NetworkCommand(Commands.BeginCompetition, this.Competition));
            NotifyPropertyChanged("NotInProgress");
        }

        /// <summary>
        /// Handles the reconnect command.
        /// </summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void ReconnectHandler(object sender, ExecutedRoutedEventArgs e)
        {
            _serverListener.Reconnect();
        }

        /// <summary>
        /// Decides if the Reconnect command may be executed.
        /// </summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void CanHandleReconnect(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = !_serverListener.TcpClient.Connected;
        }

        /// <summary>
        /// Handles the RemoveCompetitor command. A listbox, with the diver to remove being the selected element should be passed as a parameter.
        /// </summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void RemoveCompetitorHandler(object sender, ExecutedRoutedEventArgs e)
        {
            var listBox = e.Parameter as ListBox;
            if (listBox != null)
	        {
		        var item = (listBox.SelectedItem as Diver);
                if (item != null)
	            {
		            var inputBox = new InputBox("Är du säker?",
                    String.Format("Är du säker på att du vill ta bort {0}?",
                    (item.Name)));

                    if (inputBox.ShowDialog() == true)
                    {
                        Competition.RemoveParticipant(item);
                        listBox.SelectedIndex = listBox.Items.Count - 1;
                        listBox.UnselectAll();
                        listBox.UpdateLayout();
                    }
	            }
	        }  
        }

        /// <summary>
        /// Decides if the RemoveCompetitor command can be executed. A listbox, with the diver to remove being the selected element should be passed as a parameter.
        /// </summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void CanHandleRemoveCompetitor(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = false;
            var listBox = (e.Parameter as ListBox);
            if (listBox != null)
            {
                e.CanExecute = (listBox.SelectedItem != null);
            }
        }
        /// <summary>
        /// Handles the RemoveJudge command. A listbox, with the Judge to remove being the selected element should be passed as a parameter.
        /// </summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void RemoveJudgeHandler(object sender, ExecutedRoutedEventArgs e)
        {
            var listBox = e.Parameter as ListBox;
            if (listBox != null)
            {
                var item = (listBox.SelectedItem as User);
                if (item != null)
                {
                    var inputBox = new InputBox("Är du säker?",
                    String.Format("Är du säker på att du vill ta bort {0}?",
                    (item.Name)));

                    if (inputBox.ShowDialog() == true)
                    {
                        Competition.RemoveJudge(item);
                        listBox.SelectedIndex = listBox.Items.Count - 1;
                        listBox.UnselectAll();
                        listBox.UpdateLayout();
                    }
                }
            } 
        }
        /// <summary>
        /// Decides if the RemoveJudge command can be executed. A listbox, with the Judge to remove being the selected element should be passed as a parameter.
        /// </summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void CanHandleRemoveJudge(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = false;
            var listBox = (e.Parameter as ListBox);
            if (listBox != null)
            {
                e.CanExecute = (listBox.SelectedItem != null);
            }
        }
        /// <summary>
        /// Not implemented. @deprecated @warning DO NOT CALL.
        /// </summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void RemoveHeadJudgeHandler(object sender, ExecutedRoutedEventArgs e)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Not implemented. @deprecated @warning DO NOT CALL.
        /// </summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void CanHandleRemoveHeadJudge(object sender, CanExecuteRoutedEventArgs e)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// Handles the SetActiveCompetition command. A listbox, with a KeyValuePair<int CompetitionID, string Description> as selected item must be passed as parameter.
        /// </summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void SetActiveCompetitionHandler(object sender, ExecutedRoutedEventArgs e)
        {
            var p = (e.Parameter as ListBox);

            if(p != null)
            {
                var selectedCompetition = (KeyValuePair<int, String>)p.SelectedValue;
               _serverListener.SendObject(new NetworkCommand(Commands.SetCompetition, selectedCompetition.Key));
            }
        }
        /// <summary>
        /// Decides if  the SetActiveCompetition command can be executed. A listbox, with a KeyValuePair<int CompetitionID, string Description> as selected item must be passed as parameter.
        /// </summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void CanHandleSetActiveCompetition(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = false;
            var p = e.Parameter as ListBox;
            if(p.SelectedIndex != -1)
                e.CanExecute = true;
        }

        /// <summary>
        /// Handles the CreateNewCompetition command.
        /// </summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void CreateNewCompetitionHandler(object sender, ExecutedRoutedEventArgs e)
        {
            _serverListener.SendObject(new NetworkCommand(Commands.SetCompetition, -1));
        }
        /// <summary>
        /// Decides if the CreateNewCompetition command can be executed. 
        /// </summary>
        /// <param name="sender">The invoking object</param>
        /// <param name="e">The event arguments</param>
        protected override void CanHandleCreateNewCompetition(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }
    }
}
